package main

import (
	"context"
	"flag"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/spf13/viper"
	"github.com/topfreegames/pitaya"
	"github.com/topfreegames/pitaya/acceptor"
	"github.com/topfreegames/pitaya/cluster"
	"github.com/topfreegames/pitaya/component"
	"github.com/topfreegames/pitaya/fairy/constants"
	"github.com/topfreegames/pitaya/fairy/dao"
	"github.com/topfreegames/pitaya/fairy/manager"
	protos2 "github.com/topfreegames/pitaya/fairy/protos"
	"github.com/topfreegames/pitaya/fairy/services/game"
	"github.com/topfreegames/pitaya/fairy/services/hall"
	"github.com/topfreegames/pitaya/fairy/services/player"
	"github.com/topfreegames/pitaya/fairy/services/world"
	"github.com/topfreegames/pitaya/fairy/template"
	"github.com/topfreegames/pitaya/logger"
	"github.com/topfreegames/pitaya/modules"
	"github.com/topfreegames/pitaya/route"
	"github.com/topfreegames/pitaya/serialize/protobuf"
	"github.com/topfreegames/pitaya/session"
	"strings"
)



func configureFrontend(port int) {
	tcp := acceptor.NewTCPAcceptor(fmt.Sprintf(":%d", port))
	err := pitaya.AddRoute("room", func(
		ctx context.Context,
		route *route.Route,
		payload []byte,
		servers map[string]*cluster.Server,
	) (*cluster.Server, error) {
		// will return the first server
		for k := range servers {
			return servers[k], nil
		}
		return nil, nil
	})

	if err != nil {
		fmt.Printf("error adding route %s\n", err.Error())
	}

	err = pitaya.SetDictionary(map[string]uint16{
		"connector.getsessiondata": 1,
		"connector.setsessiondata": 2,
		"room.room.getsessiondata": 3,
		"onMessage":                4,
		"onMembers":                5,
	})

	if err != nil {
		fmt.Printf("error setting route dictionary %s\n", err.Error())
	}

	pitaya.AddAcceptor(tcp)

	//session创建时，从world服务获取一个唯一的sessionId进行绑定
	session.OnSessionCreate(func(s *session.Session) error {
		ctx := context.Background()
		reply := &protos2.GatePOnSessionCreatedRet{}
		err = pitaya.RPC(ctx,constants.GatePOnSessionCreated,reply,&protos2.GatePOnSessionCreated{})
		if err != nil {
			logger.Log.Errorf("session OnSessionCreate failed,error=%s",err.Error())
			return err
		}

		err = s.Bind(ctx,*reply.SessionId)
		if err != nil {
			logger.Log.Errorf("session OnSessionCreate failed,error=%s",err.Error())
			return err
		}

		return nil
	})

	//session关闭时，给world服务推送一条消息 world服务器根据玩家在登录服 游戏服 做对应的处理
	session.OnSessionClose(func(s *session.Session) {
	reply := &protos2.GatePOnSessionClosedRet{}
	err1 := pitaya.RPC(context.Background(),constants.GateWOnSessionClosed,reply,&protos2.GatePOnSessionClosed{
		SessionIds: []string{s.UID()},
	})
	if err1 != nil {
		logger.Log.Errorf("OnSessionClose failed,error=%s",err1.Error())
		return
	}
	})
}

func configureHall() {
	//读取模板文件
	err := template.Init()
	if err != nil {
		fmt.Printf("error template Init %s\n", err.Error())
	}
	fmt.Println("加载模板文件完成！")

	login := hall.NewLogin()
	pitaya.Register(login,
		component.WithName("login"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(login,
		component.WithName("login"),
		component.WithNameFunc(strings.ToLower),
	)

	role := hall.NewRole()
	pitaya.Register(role,
		component.WithName("role"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(role,
		component.WithName("role"),
		component.WithNameFunc(strings.ToLower),
	)

	game := hall.NewGame()
	pitaya.Register(game,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(game,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)

	//处理session关闭
	gate := hall.NewGateDealer()
	pitaya.Register(gate,
		component.WithName("gate"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(gate,
		component.WithName("gate"),
		component.WithNameFunc(strings.ToLower),
	)

	//注册dao
	pitaya.RegisterDao(dao.NewGuidDao())
	pitaya.RegisterDao(dao.NewAccountDataDao())
	pitaya.RegisterDao(dao.NewPlayerDataDao())
	pitaya.RegisterDao(dao.NewGlobalDataDao())
	pitaya.RegisterDao(dao.NewPlayerMailBoxDao())

	//注册管理器
	pitaya.RegisterManger(manager.NewPlayerManager())
	pitaya.RegisterManger(manager.NewGuiGeneratorManager())
	pitaya.RegisterManger(manager.NewLoginManger())
	pitaya.RegisterManger(manager.NewPlayerMailBoxManager())
	pitaya.RegisterManger(manager.NewGMAccountManager())

	//注册线程
	loginThread := manager.NewLoginThread()
	pitaya.RegisterThread(loginThread,
		component.WithName(constants.LoginThread),
		component.WithNameFunc(strings.ToLower))
}



func configureGame()  {
	err := template.Init()
	if err != nil {
		fmt.Printf("error template Init %s\n", err.Error())
	}

	logic := game.NewLogic()
	pitaya.Register(logic,
		component.WithName("logic"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(logic,
		component.WithName("logic"),
		component.WithNameFunc(strings.ToLower),
	)

	//注册管理器
	pitaya.RegisterManger(manager.NewTimeManager())
	pitaya.RegisterManger(manager.NewSpecialEventManager())

	//注册协程
	daemonThread := manager.NewDaemonThread()
	pitaya.RegisterThread(daemonThread,
		component.WithName(constants.DaemonThread),
		component.WithNameFunc(strings.ToLower))
}

func configureWorld ()  {
	err := template.Init()
	if err != nil {
		fmt.Printf("error template Init %s\n", err.Error())
	}

	hallDealer := world.NewHallDealer()
	pitaya.Register(hallDealer,
		component.WithName("hall"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(hallDealer,
		component.WithName("hall"),
		component.WithNameFunc(strings.ToLower),
	)

	gameDealer := world.NewGameDealer()
	pitaya.Register(gameDealer,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(gameDealer,
		component.WithName("game"),
		component.WithNameFunc(strings.ToLower),
	)

	gateDealer := world.NewGateDealer()
	pitaya.Register(gateDealer,
		component.WithName("gate"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(gateDealer,
		component.WithName("gate"),
		component.WithNameFunc(strings.ToLower),
	)

	//注册dao
	pitaya.RegisterDao(dao.NewPlayerDataDao())

	//注册管理器
	pitaya.RegisterManger(manager.NewPlayerManager())
	pitaya.RegisterManger(manager.NewPlayerNameManager())
}

/**
玩家管理服务
 */
func configurePlayer()  {
	playerDealer := player.NewPlayerDealer()
	pitaya.Register(playerDealer,
		component.WithName("player"),
		component.WithNameFunc(strings.ToLower),
	)
	pitaya.RegisterRemote(playerDealer,
		component.WithName("player"),
		component.WithNameFunc(strings.ToLower),
	)

	//注册管理器
	pitaya.RegisterManger(manager.NewOnlinePlayerManager())
}

func main() {
	port := flag.Int("port", 3250, "the port to listen")
	svType := flag.String("type", "connector", "the server type")
	isFrontend := flag.Bool("frontend", true, "if server is frontend")
	//rpcServerPort := flag.String("rpcsvport", "4222", "the port that grpc server will listen")

	flag.Parse()

	defer pitaya.Shutdown()

	pitaya.SetSerializer(protobuf.NewSerializer())
	confs := viper.New()
	//confs.Set("pitaya.cluster.rpc.server.grpc.port", *rpcServerPort)

	meta := map[string]string{
		//constants.GRPCHostKey: "127.0.0.1",
		//constants.GRPCPortKey: *rpcServerPort,
	}
	pitaya.Configure(*isFrontend, *svType, pitaya.Cluster, meta, confs)

	bs := modules.NewETCDBindingStorage(pitaya.GetServer(), pitaya.GetConfig())
	pitaya.RegisterModule(bs, "bindingsStorage")

	//注册数据库
	ds,err1 := modules.NewDatabaseStorage(pitaya.GetConfig())
	if err1 != nil {
		panic(err1)
	}
	pitaya.RegisterModule(ds,"databaseStorage")

	//注册redis
	rs := modules.NewRedisStorage(pitaya.GetConfig())
	pitaya.RegisterModule(rs,"redisStorage")

	switch *svType {
	case constants.SvTypeConnector:
		configureFrontend(*port)
	case constants.SvTypeHall:
		configureHall()
	case constants.SvTypeGame:
		configureGame()
	case constants.SvTypeWorld:
		configureWorld()
	case constants.SvTypePlayer:
		configurePlayer()
	default:
		fmt.Printf("error svType %s\n", *svType)
		return
	}

	ns, err := cluster.NewNatsRPCServer(pitaya.GetConfig(), pitaya.GetServer(), pitaya.GetMetricsReporters(), pitaya.GetDieChan())
	if err != nil {
		panic(err)
	}

	nc, err := cluster.NewNatsRPCClient(
		pitaya.GetConfig(),
		pitaya.GetServer(),
		pitaya.GetMetricsReporters(),
		pitaya.GetDieChan(),
	)
	if err != nil {
		panic(err)
	}
	pitaya.SetRPCServer(ns)
	pitaya.SetRPCClient(nc)
	pitaya.Start()
	//protoc ./proto/login.proto --go_out=./
	//protoc ./proto/inner_message_common.proto ./proto/inner_message_lw_msg.proto --go_out=./
}